package com.gitee.huanminabc.jcommon.base;

import com.gitee.huanminabc.jcommon.container.ArrayUtil;
import com.gitee.huanminabc.jcommon.exception.CommonException;
import com.gitee.huanminabc.jcommon.multithreading.executor.SleepTools;
import com.gitee.huanminabc.jcommon.test.MockData;
import lombok.extern.slf4j.Slf4j;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * 重试指定的操作
 * <p>retry some operation with specified Strategy until some conditions are met.(使用指定的策略重试某些操作，直到满足某些条件)</p>
 *
 * @param <T> 操作所返回的数据类型
 * @author cuishuanghui
 * 2024-12-11
 */
@Slf4j
public class RetryUtil<T> {

    /**
     * 最大重试次数
     */
    private static final int DEFAULT_MAX_RETRIES = 3;
    /**
     * 默认延迟时间
     */
    private static final int DEFAULT_RETRY_DELAY = 50;


    /**
     * 重试操作
     */
    private Supplier<T> operation;
    /**
     * 最大重试次数
     */
    private int maxRetries = DEFAULT_MAX_RETRIES; // 默认值
    /**
     * 最小延迟时间
     */
    private int minDelay = DEFAULT_RETRY_DELAY; // 默认值
    /**
     * 最大延迟时间
     */
    private int maxDelay = DEFAULT_RETRY_DELAY; // 默认值
    /**
     * 验证返回值的函数
     */
    private Function<T, Boolean> validateFunction;
    /**
     * 指定重试的异常类型
     */
    private Class<? extends Exception>[] retryExceptions;
    //如果重试都没满足是否抛出异常
    private boolean throwException = false;

    private RetryUtil() {
    }

    public static <T> RetryUtil<T> create(Supplier<T> operation) {
        RetryUtil<T> RetryUtil = new RetryUtil<>();
        RetryUtil.operation = operation;
        return RetryUtil;
    }

    public RetryUtil<T> withRetryStrategy(int maxRetries, int minDelay, int maxDelay) {
        Assert.isTrue(maxDelay>=minDelay, "最大延迟时间必须大于等于最小延迟时间");
        this.maxRetries = maxRetries;
        this.minDelay = minDelay;
        this.maxDelay = maxDelay;
        return this;
    }

    public RetryUtil<T> withRetryStrategy(int maxRetries, int delay) {
        return withRetryStrategy(maxRetries, delay, delay);
    }

    //返回值为true时结束重试 返回值为false时继续重试  如果有异常则需要根据异常类型判断是否结束重试
    @SafeVarargs
    public final RetryUtil<T> withEndFlag(Function<T, Boolean> validateFunction, Class<? extends Exception>... exceptions) {
        this.validateFunction = validateFunction;
        this.retryExceptions = exceptions;
        return this;
    }
    @SafeVarargs
    public final RetryUtil<T> withEndFlag(Function<T, Boolean> validateFunction,boolean retryFailThrowException, Class<? extends Exception>... exceptions) {
        this.validateFunction = validateFunction;
        this.retryExceptions = exceptions;
        this.throwException = retryFailThrowException;
        return this;
    }

    /**
     * 执行重试操作
     *
     * @return 返回操作结果
     */
    public T execute() {
        Assert.notNull(operation, "请先设置要执行的操作");
        Assert.isTrue(validateFunction != null || retryExceptions != null, "至少需要设置一个重试条件");

        for (int attempt = 0; attempt <= maxRetries; attempt++) {
            try {
                T result = operation.get();
                if (validateFunction == null || validateFunction.apply(result)) {
                    return result; // 成功结果或无验证
                }

                log.warn("验证失败，尝试次数 {}: {}", attempt, result);
                tryDelay(attempt);
            } catch (Exception e) {
                if (ArrayUtil.isEmpty(retryExceptions) || !ArrayUtil.contains(retryExceptions, e.getClass())) {
                    throw e; // 如果异常不在重试列表中，则抛出
                }
                log.warn("尝试 {} 失败: {}", attempt, e.getMessage());
                tryDelay(attempt);
            }
        }
        //  超过最大重试次数抛出异常， （抛异常还是）返回空
        if (throwException){
            throw new CommonException("重试了{}次还是失败",maxRetries);
        }
        return null;
    }

    private void tryDelay(int attempt) {
        if (attempt < maxRetries) {
            if (minDelay == maxDelay) SleepTools.second(minDelay);
            else SleepTools.second(MockData.getInteger(minDelay, maxDelay));

        }
    }

    public static void main(String[] args) {
//        Retryable<Integer> retryable = () -> {
//            // 模拟操作
//            if (Math.random() < 0.9) throw new CommonException("模拟异常");
//            return 0;
//        };
//
//        RetryStrategy<Integer> retryStrategy = RetryStrategy.create(retryable)
//            .withMaxRetries(3)
//            .withDelay(100, 200)
//            .withValidation(result -> result == 1, CommonException.class);
//
//        try {
//            System.out.println("结果: " + retryStrategy.executeWithRetry());
//        } catch (RetryException e) {
//            System.err.println("操作失败: " + e.getMessage());
//        }
    }
}